package com.starry.module.system.core.user.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.collection.CollectionUtil;
import cn.hutool.core.util.ObjectUtil;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.starry.core.common.constants.CommonError;
import com.starry.core.common.domain.vo.PageVo;
import com.starry.core.common.execption.ServiceException;
import com.starry.core.common.tenant.context.TenantContextHolder;
import com.starry.core.security.context.SecurityInfoContext;
import com.starry.core.security.domain.BaseUser;
import com.starry.core.tenant.core.aop.TenantIgnore;
import com.starry.db.core.enums.DBEnableDisableStatusEnum;
import com.starry.db.core.query.LambdaQueryWrapperX;
import com.starry.module.system.api.user.domain.qo.*;
import com.starry.module.system.api.user.domain.vo.SysUserDtlVo;
import com.starry.module.system.api.user.domain.vo.SysUserLoginInfoVo;
import com.starry.module.system.api.user.domain.vo.SysUserVo;
import com.starry.module.system.core.dept.service.SysDeptService;
import com.starry.module.system.core.post.service.SysPostService;
import com.starry.module.system.core.role.service.SysRoleService;
import com.starry.module.system.core.tenant.service.SysTenantService;
import com.starry.module.system.core.user.entity.SysUser;
import com.starry.module.system.core.user.mapper.SysUserMapper;
import com.starry.module.system.core.user.service.SysUserService;
import org.springframework.context.annotation.Lazy;
import org.springframework.security.crypto.password.PasswordEncoder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

/**
 * 系统用户-服务实现
 *
 * @author xiaoke
 */
@Service("sysUserService")
public class SysUserServiceImpl implements SysUserService {

    private final SysUserMapper sysUserMapper;

    private final SysTenantService sysTenantService;

    private final SysPostService sysPostService;

    private final SysRoleService sysRoleService;

    private final SysDeptService sysDeptService;

    private final PasswordEncoder passwordEncoder;

    @Lazy
    public SysUserServiceImpl(SysUserMapper sysUserMapper, SysTenantService sysTenantService, SysPostService sysPostService, SysRoleService sysRoleService, SysDeptService sysDeptService, PasswordEncoder passwordEncoder) {
        this.sysUserMapper = sysUserMapper;
        this.sysTenantService = sysTenantService;
        this.sysPostService = sysPostService;
        this.sysRoleService = sysRoleService;
        this.sysDeptService = sysDeptService;
        this.passwordEncoder = passwordEncoder;
    }

    @Override
    public PageVo<SysUserVo> page(SysUserPageQo pageQo) {
        pageQo.setPageNum((pageQo.getPageNum() - 1) * pageQo.getPageSize());
        Long total = sysUserMapper.selectPageTotal(pageQo);
        if (total == 0) {
            return new PageVo<>(List.of(), 0L);
        }
        List<SysUserVo> userList = sysUserMapper.selectPageUser(pageQo);
        return new PageVo<>(userList, total);
    }

    @Override
    public SysUserDtlVo getByIdDtl(Long id) {
        SysUserDtlVo sysUser = sysUserMapper.selectDtlById(id);
        if (sysUser == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }
        return sysUser;
    }

    @Override
    public SysUser getById(Long id) {
        return sysUserMapper.selectById(id);
    }

    @Override
    public SysUser getByValue(String value) {
        return sysUserMapper.selectByValue(value);
    }

    @Transactional
    @Override
    public Long save(SysUserQo sysUserQo) {
        // 校验租户账号是否已满
        validateUserCount();
        validate(sysUserQo);

        LambdaQueryWrapperX<SysUser> wrapper = new LambdaQueryWrapperX<>();
        wrapper
                .eq(SysUser::getUsername, sysUserQo.getUsername());
        if (sysUserMapper.selectCount(wrapper) > 0) {
            throw new ServiceException(CommonError.DATA_EXISTS.getMsg());
        }

        // 密码加密
        if (ObjectUtil.isNotEmpty(sysUserQo.getPassword())) {
            sysUserQo.setPassword(passwordEncoder.encode(sysUserQo.getPassword()));
        }

        SysUser sysUser = BeanUtil.toBean(sysUserQo, SysUser.class);

        sysUser.setId(null);
        sysUserMapper.insert(sysUser);

        insertBatchUserPost(sysUser.getId(), sysUserQo.getPostIdList());
        insetBatchUserRole(sysUser.getId(), sysUserQo.getRoleIdList());
        insetBatchUserDept(sysUser.getId(), sysUserQo.getDeptIdList());

        return sysUser.getId();
    }

    @Override
    public Set<String> getByIdUserPerms(Long userId) {
        return sysUserMapper.getByIdUserPerms(userId);
    }

    @Override
    public boolean existUser(Long userId) {
        return sysUserMapper.selectCount(SysUser::getId, userId) > 0;
    }

    @Override
    public boolean existUserList(Set<Long> userIdList) {
        if (CollUtil.isEmpty(userIdList)) return true;
        LambdaQueryWrapperX<SysUser> queryWrapper = new LambdaQueryWrapperX<>();
        queryWrapper.in(SysUser::getId, userIdList);
        return sysUserMapper.selectCount(queryWrapper) == userIdList.size();
    }

    @Transactional
    @Override
    public void updateById(SysUserQo sysUserQo) {
        if (sysUserQo.getId() == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }
        // 校验
        validate(sysUserQo);

        LambdaQueryWrapperX<SysUser> wrapper = new LambdaQueryWrapperX<>();
        wrapper
                .ne(SysUser::getId, sysUserQo.getId())
                .and(w ->
                        w.eq(SysUser::getUsername, sysUserQo.getUsername())
                );
        if (sysUserMapper.selectCount(wrapper) > 0) {
            throw new ServiceException(CommonError.DATA_EXISTS.getMsg());
        }
        SysUser sysUser = sysUserMapper.selectById(sysUserQo.getId());
        if (sysUser == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }

        BeanUtil.copyProperties(sysUserQo, sysUser);

        // 密码加密
        if (ObjectUtil.isNotEmpty(sysUserQo.getPassword())) {
            sysUserQo.setPassword(passwordEncoder.encode(sysUserQo.getPassword()));
        }
        // 更新岗位
        updateUserPost(sysUser.getId(), sysUserQo.getPostIdList());
        // 更新角色
        updateUserRole(sysUser.getId(), sysUserQo.getRoleIdList());
        // 更新机构
        updateUserDept(sysUser.getId(), sysUserQo.getDeptIdList());

        sysUserMapper.updateById(sysUser);

        // 更新岗位、机构
        LambdaUpdateWrapper<SysUser> updateWrapper = new LambdaUpdateWrapper<>();
        updateWrapper
                .eq(SysUser::getId, sysUser.getId())
                .set(SysUser::getPostId, sysUserQo.getPostId())
                .set(SysUser::getDeptId, sysUserQo.getDeptId());
        sysUserMapper.update(null, updateWrapper);
    }

    @Transactional
    @Override
    public void removeByIds(Set<Long> idList) {
        if (idList.isEmpty()) return;
        if (sysUserMapper.validateIsTenantUser(idList)) {
            throw new ServiceException("租户默认账号不允许删除");
        }
        sysUserMapper.deleteByIds(idList);
    }

    @Transactional
    @Override
    public void enable(Set<Long> idList) {
        if (idList.isEmpty()) return;
        sysUserMapper.updateBatch(setUserStatus(idList, DBEnableDisableStatusEnum.ENABLE));
    }

    @Transactional
    @Override
    public void disable(Set<Long> idList) {
        if (idList.isEmpty()) return;
        sysUserMapper.updateBatch(setUserStatus(idList, DBEnableDisableStatusEnum.DISABLE));
    }

    @Override
    public SysUser initialUser(String username) {
        SysUser user = sysUserMapper.selectByValue(username);
        if (user != null) return user;
        // 校验租户账号是否已满
        validateUserCount();
        user = new SysUser();
        user.setUsername(username);
        user.setPhone(username);
        user.setDeptId(0L);
        user.setStatus(DBEnableDisableStatusEnum.ENABLE.getCode());
        sysUserMapper.insert(user);
        return user;
    }

    @Override
    public SysUser initialUser(SysUser user) {
        // 校验租户账号是否已满
        validateUserCount();
        sysUserMapper.insert(user);
        return user;
    }

    @Override
    @TenantIgnore
    public SysUserLoginInfoVo getByLoginUserInfo() {
        Long userId = SecurityInfoContext.getUserId();
        SysUser sysUser = sysUserMapper.selectById(userId);
        if (sysUser == null) {
            throw new ServiceException(CommonError.DATA_NOT_EXISTS.getMsg());
        }
        SysUserLoginInfoVo dtl = BeanUtil.toBean(sysUser, SysUserLoginInfoVo.class);
        dtl.setPostList(sysPostService.getByUserIdList(userId));
        dtl.setRoleList(sysRoleService.getByUserIdList(userId));
        dtl.setDeptList(sysDeptService.getByUserIdList(userId));
        dtl.setPermsList(sysUserMapper.getByIdUserPerms(userId));
        return dtl;
    }

    @Override
    public void saveTenantUserAdmin(Set<Long> roleIdList) {
        SysUser sysUser = new SysUser();
        sysUser.setTenantAdmin(true);
        sysUser.setUsername("admin");
        sysUser.setName("超级管理员");
        sysUser.setPassword(passwordEncoder.encode("123456"));

        sysUserMapper.insert(sysUser);

        insetBatchUserRole(sysUser.getId(), roleIdList);
    }

    @Override
    public List<SysUserVo> findList(SysUserPageQo pageQo) {
        return BeanUtil.copyToList(sysUserMapper.selectUserList(pageQo), SysUserVo.class);
    }

    @Override
    public void updateLogin(SysUserLoginQo updateLoginQo) {
        BaseUser loginUser = SecurityInfoContext.getLoginUser();
        if (loginUser == null) {
            throw new ServiceException("登录信息已失效，请重新登录");
        }
        SysUser user = BeanUtil.toBean(updateLoginQo, SysUser.class);
        user.setId(loginUser.getId());
        sysUserMapper.updateById(user);
    }

    @Override
    public void updateLoginInfo(SysUserLoginInfoQo loginInfoQo) {
        if (loginInfoQo == null) {
            return;
        }
        SysUser sysUser = BeanUtil.toBean(loginInfoQo, SysUser.class);
        sysUserMapper.updateById(sysUser);
    }

    @Override
    public void updatePassword(SysUserPasswordQo passwordQo) {
        if (!passwordQo.getPassword().equals(passwordQo.getConfirmPassword())) {
            throw new ServiceException("密码与确认密码不一致");
        }
        SysUser user = new SysUser();
        user.setId(SecurityInfoContext.getUserId());
        user.setPassword(passwordEncoder.encode(passwordQo.getPassword()));
        sysUserMapper.updateById(user);
    }

    /**
     * 批量插入用户岗位
     *
     * @param userId     userId
     * @param postIdList postIdList
     */
    private void insertBatchUserPost(Long userId, Set<Long> postIdList) {
        if (postIdList == null || postIdList.isEmpty()) return;
        BaseUser loginUser = SecurityInfoContext.getLoginUser();
        sysUserMapper.insertBatchUserPost(userId, postIdList, LocalDateTime.now(), loginUser.getId(), loginUser.getUsername());
    }

    /**
     * 更新用户岗位
     *
     * @param userId     userId
     * @param postIdList postIdList
     */
    private void updateUserPost(Long userId, Set<Long> postIdList) {
        Set<Long> dbPostIds = sysUserMapper.selectByUserIdPostIdList(userId);
        // 计算新增和删除的岗位编号
        Set<Long> postIds = CollUtil.emptyIfNull(postIdList);
        Collection<Long> createPostIds = CollUtil.subtract(postIds, dbPostIds);
        Collection<Long> deletePostIds = CollUtil.subtract(dbPostIds, postIds);

        // 执行新增和删除。对于已经授权的岗位，不用做任何处理
        if (!CollectionUtil.isEmpty(createPostIds)) {
            BaseUser loginUser = SecurityInfoContext.getLoginUser();
            sysUserMapper.insertBatchUserPost(userId, new HashSet<>(createPostIds), LocalDateTime.now(), loginUser.getId(), loginUser.getUsername());
        }
        if (!CollectionUtil.isEmpty(deletePostIds)) {
            sysUserMapper.deleteBatchUserPost(userId, new HashSet<>(deletePostIds));
        }
    }

    /**
     * 更新用户角色
     *
     * @param userId     userId
     * @param roleIdList roleIdList
     */
    private void updateUserRole(Long userId, Set<Long> roleIdList) {
        Set<Long> dbPostIds = sysUserMapper.selectByUserIdRoleIdList(userId);
        // 计算新增和删除的角色编号
        Set<Long> roleIds = CollUtil.emptyIfNull(roleIdList);
        Collection<Long> createPostIds = CollUtil.subtract(roleIds, dbPostIds);
        Collection<Long> deletePostIds = CollUtil.subtract(dbPostIds, roleIds);

        // 执行新增和删除。对于已经授权的角色，不用做任何处理
        if (!CollectionUtil.isEmpty(createPostIds)) {
            BaseUser loginUser = SecurityInfoContext.getLoginUser();
            sysUserMapper.insertBatchUserRole(userId, new HashSet<>(createPostIds), LocalDateTime.now(), loginUser.getId(), loginUser.getUsername());
        }
        if (!CollectionUtil.isEmpty(deletePostIds)) {
            sysUserMapper.deleteBatchUserRole(userId, new HashSet<>(deletePostIds));
        }
    }

    /**
     * 更新用户机构
     *
     * @param userId     userId
     * @param deptIdList deptIdList
     */
    private void updateUserDept(Long userId, Set<Long> deptIdList) {
        Set<Long> dbDeptIds = sysUserMapper.selectByUserIdDeptIdList(userId);
        // 计算新增和删除的部门编号
        Set<Long> deptIds = CollUtil.emptyIfNull(deptIdList);
        Collection<Long> createDeptIds = CollUtil.subtract(deptIds, dbDeptIds);
        Collection<Long> deleteDeptIds = CollUtil.subtract(dbDeptIds, deptIds);

        // 执行新增和删除。对于已经授权的角色，不用做任何处理
        if (!CollectionUtil.isEmpty(createDeptIds)) {
            BaseUser loginUser = SecurityInfoContext.getLoginUser();
            sysUserMapper.insertBatchUserDept(userId, new HashSet<>(createDeptIds), LocalDateTime.now(), loginUser.getId(), loginUser.getUsername());
        }
        if (!CollectionUtil.isEmpty(deleteDeptIds)) {
            sysUserMapper.deleteBatchUserDept(userId, new HashSet<>(deleteDeptIds));
        }
    }


    /**
     * 批量插入用户角色
     *
     * @param userId     userId
     * @param roleIdList roleIdList
     */
    private void insetBatchUserRole(Long userId, Set<Long> roleIdList) {
        if (roleIdList == null || roleIdList.isEmpty()) return;
        BaseUser loginUser = SecurityInfoContext.getLoginUser();
        sysUserMapper.insertBatchUserRole(userId, roleIdList, LocalDateTime.now(), loginUser.getId(), loginUser.getUsername());
    }

    /**
     * 批量插入用户机构
     *
     * @param userId     userId
     * @param deptIdList deptIdList
     */
    private void insetBatchUserDept(Long userId, Set<Long> deptIdList) {
        if (deptIdList == null || deptIdList.isEmpty()) return;
        BaseUser loginUser = SecurityInfoContext.getLoginUser();
        sysUserMapper.insertBatchUserDept(userId, deptIdList, LocalDateTime.now(), loginUser.getId(), loginUser.getUsername());
    }

    /**
     * 校验
     *
     * @param sysUserQo sysUserQo
     */
    private void validate(SysUserQo sysUserQo) {
        if (sysUserQo.getPostIdList() != null) {
            if (!sysPostService.existPostList(sysUserQo.getPostIdList())) {
                throw new ServiceException("岗位不存在");
            }
            if (!sysPostService.exitsPostListStatus(sysUserQo.getPostIdList())) {
                throw new ServiceException("岗位未启用");
            }
        }
        if (sysUserQo.getRoleIdList() != null) {
            if (!sysRoleService.existRoleList(sysUserQo.getRoleIdList())) {
                throw new ServiceException("角色不存在");
            }
            if (!sysRoleService.exitsRoleListStaus(sysUserQo.getRoleIdList())) {
                throw new ServiceException("角色未启用");
            }
        }
        if (sysUserQo.getDeptId() != null) {
            if (!sysDeptService.existDeptList(sysUserQo.getDeptIdList())) {
                throw new ServiceException("机构不存在");
            }
            if (!sysDeptService.existDeptListStatus(sysUserQo.getDeptIdList())) {
                throw new ServiceException("机构未启用");
            }
        }
    }


    /**
     * 校验租户账号是否已满
     */
    private void validateUserCount() {
        Long userCount = sysUserMapper.selectCount(SysUser::getTenantId, TenantContextHolder.getRequiredTenantId());
        if (!sysTenantService.validateAccountCount(userCount, TenantContextHolder.getRequiredTenantId())) {
            throw new ServiceException("当前租户账号已满，无法新增");
        }
    }

    private Set<SysUser> setUserStatus(Set<Long> idList, DBEnableDisableStatusEnum status) {
        Set<SysUser> sysUsers = new HashSet<>();
        idList.forEach(item -> {
            SysUser sysUser = new SysUser();
            sysUser.setId(item);
            sysUser.setStatus(status.getCode());
            sysUsers.add(sysUser);
        });
        return sysUsers;
    }

}

